<!DOCTYPE html>
<html class="editor_html">
<head>
    <meta charset="utf-8">
    <title>md编辑器</title>
    <link rel="stylesheet" href="index.css">
</head>
<body class="editor_body">
	<table width="500" border="0">
		<tr>
			<td width="25%"><input type="file" name="upload" id="upload" class="upload" /></td>
			<td width="25%"><button id="create" class="create" onclick="converst_to_html()" >转换为html</button></td>
			<td width="25%"><button id="save_txt" class="create" onclick="save_to_txt()" >保存md文件</button></td>
			<td width="25%"><button id="loacl_preview" class="create" onclick="local_preview()" >全页面预览</button></td>
		</tr>
	</table>
	<textarea name="content" id="content" spellcheck="false" class="content"></textarea>
	<div id="right_div" class="editor_div" > </div>
	<script type="text/javascript">
	    var getFileContent = function(fileInput, callback) {
	    	if (fileInput.files && fileInput.files.length > 0 && fileInput.files[0].size > 0)  {
	        	var file = fileInput.files[0];
	        	if (window.FileReader) { //检测浏览器对FlieReader是否支持。
	        	    var reader = new FileReader(); //实例化。
	        	    reader.onloadend = function(event) { // 	onloadend事件为：无论文件是否读取成功，读取完后触发。
	        	        if (event.target.readyState == FileReader.DONE) {
	        	            // readyState属性有三个值：0、1、2；
	        	            // 0 表示的状态为EMPTY（Reader has been created. None of the 	read  methods called yet.）；
	        	            // 1 表示的状态为LOADING （A readAs method was invoked. A File 	or  Blob is being read, and no error has occurred yet.）
	        	            // 2 表示的状态为DONE （The read operation is complete.）
	        	            callback(event.target.result); //  	注意：文件读取是否成功，结果都会存储到result属性中。
	        	        }
	        	    };
	        	    reader.readAsText(file, 'UTF-8');
	        	}
	        else {alert("浏览器不支持FlieReader");}
	   		}
	   };

	function fakeClick(obj) {
	   var ev = document.createEvent("MouseEvents");
	   ev.initMouseEvent("click", true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
	   obj.dispatchEvent(ev);
	}

	function exportRaw(name, data) {
	                  var urlObject = window.URL || window.webkitURL || window;
	                  var export_blob = new Blob([data]);
	                  var save_link = document.createElementNS("http://www.w3.org/1999/xhtml", "a")
	                  save_link.href = urlObject.createObjectURL(export_blob);
	                  save_link.download = name;
	                  fakeClick(save_link);
	}
	function $(selector) {
	    return document.querySelector(selector);
	}

	function Markdown(textarea, div) {
	    this.textarea = textarea;
	    this.div = div;
	}

	Markdown.prototype = {
	    init: function() {
	        this.div.innerHTML = this.compile(this.textarea.value);
	        this.textarea.oninput = () => {
	            this.div.innerHTML = this.compile(this.textarea.value);
	            view_source = 3;//3代表编辑区有改动，则渲染区的内容由编辑区获得
	        }
	    },
	    compile: function(initStr) {
	        var rows = initStr.split('\n');
	        var matchArry;
	        var html = '';
	        for (var i = 0, len = rows.length; i < len; i++) {
	            matchArry = rows[i].match(/^#\s/)
	                     || rows[i].match(/^##\s/)
	                     || rows[i].match(/^###\s/)
	                     || rows[i].match(/^####\s/)
	                     || rows[i].match(/^#####\s/)
	                     || rows[i].match(/^######\s/)
	                     || rows[i].match(/^\*{3,}/)
	                     || rows[i].match(/^>\s/)
	                     || rows[i].match(/^\*\s/)
	                     || rows[i].match(/^\d\.\s/)
	                     || rows[i].match(/^```\s*$/)
	                     || rows[i].match(/^\|.*\|/);

	            if (matchArry) {
	                switch(matchArry[0]) {
	                    case '# ':
	                        html += '<h1>' + this.format(rows[i].substring(2)) + '</h1>';
	                        break;
	                    case '## ':
	                        html += '<h2>' + this.format(rows[i].substring(3)) + '</h2>';
	                        break;
	                    case '### ':
	                        html += '<h3>' + this.format(rows[i].substring(4)) + '</h3>';
	                        break;
	                    case '#### ':
	                        html += '<h4>' + this.format(rows[i].substring(5)) + '</h4>';
	                        break;
	                    case '##### ':
	                        html += '<h5>' + this.format(rows[i].substring(6)) + '</h5>';
	                        break;
	                    case '###### ':
	                        html += '<h6>' + this.format(rows[i].substring(7)) + '</h6>';
	                        break;
	                    case rows[i].match(/^\*{3,}/) && rows[i].match(/^\*{3,}/)[0]:
	                        html += rows[i].replace(/^\*{3,}/g, '<hr>');
	                        break;
	                    case '> '://引用
	                        var temp = '';
	                        var re = /^>\s/;
	                        while (i < len && rows[i].match(re)) {
	                            temp += '<p>\n' + this.format(rows[i].substring(2, rows[i].length)) + '</p>\n';
	                            i++;
	                        }
	                        html += '<blockquote>\n' + temp + '</blockquote>\n';
	                        break;
	                    case '* '://无序列表
	                        var temp = '';
	                        var re = /^\*\s/;
	                        while (i < len && rows[i].match(re)) {
	                            temp += '<li>' + this.format(rows[i].substring(2, rows[i].length)) + '</li>\n';
	                            i++;
	                        } 
	                        html += '<ul>\n' + temp + '</ul>\n';
	                        break;
	                    case rows[i].match(/^\d+\.\s/) && rows[i].match(/^\d+\.\s/)[0]://带编号列表
	                        var temp = '';
	                        var re = /^\d+\.\s/;
	                        while (i < len && rows[i].match(re)) {
	                            temp += '<li>' + this.format(rows[i].substring(3, rows[i].length)) + '</li>\n';
	                            i++;
	                        } 
	                        html += '<ol>\n' + temp + '</ol>\n';
	                        break;
	                    case rows[i].match(/^```\s*$/) && rows[i].match(/^```\s*$/)[0]://代码块
	                        var temp = '';
	                        var re = /^```\s*$/;
	                        var str_re = '';
	                        i++;
	                        while (i < len && !re.test(rows[i])) {
	                        	str_re = rows[i].replace('<','&lt');
	                            temp += '<span>' + str_re + '</span>\n';
	                            i++;
	                        }
	                        html += '<pre>' + temp + '</pre>\n';
	                        break;
	                    case rows[i].match(/^\|.*\|/) && rows[i].match(/^\|.*\|/)[0]://表格
	                        var temp = '';
	                        var re = /^\|.*\|/;
	                        var thRe = /^\[th\]/;
	                        var align_right = /\s+---:\s+/;
	                        var align_left = /\s+:---\s+/;
	                        var align_center = /\s+:---:\s+/;
	                        var arry, j, jlen;
	                        var align_arr = new Array();

	                        arry = rows[i].split('|');
	                        for (j = 1, jlen = arry.length - 1; j < jlen; j++) {
	                        	align_arr[j] = "style='text-align: center'";//默认左对齐
	                        }
	                       
	                        while (i < len && re.test(rows[i])) {
	                            arry = rows[i].split('|');
	                            if(align_right.test(rows[i]) || 
	                            	align_center.test(rows[i]) ||
	                            	align_left.test(rows[i]) ) {//判断是否设置对齐方式
	                            	for (j = 1, jlen = arry.length - 1; j < jlen; j++) {
	                        			if(align_center.test(arry[j])){
	                        				align_arr[j] = "style='text-align: center'";
	                        			}else if (align_right.test(arry[j])){
	                        				align_arr[j] = "style='text-align: right'";
	                        			}else {
	                        				align_arr[j] = "style='text-align: left'";
	                        			}
	                            	}
	                            	i++;
	                            	continue;
	                        	 }
	                            temp += '<tr>\n';
	                            for (j = 1, jlen = arry.length - 1; j < jlen; j++) {
	                                if (thRe.test(arry[1])) {
	                                    temp += '<th '+align_arr[j] + '>' + arry[j] + '</th>\n';
	                                } else {
	                                    temp += '<td '+align_arr[j] + '>' + arry[j] + '</td>\n';
	                                }
	                            }
	                            temp += '</tr>\n';
	                            temp = temp.replace('[th]', '');
	                            i++;
	                        }
	                        html += '<table>\n' + temp + '</table>\n';
	                        break;
	                    default:
	                        break;
	                }
	            } else {
	                html += '<p>' + this.format(rows[i]) + '</p>\n';
	            }

	        }
	        return html;
	    },
	    format: function(str) { 
	        str = str.replace(/\s/g, '&nbsp;'); 

	        var bold = str.match(/\*{2}[^*].*?\*{2}/g); // 惰性匹配
	        if (bold) {
	            for (var i = 0, len = bold.length; i < len; i++) {
	                str = str.replace(bold[i], '<b>' + bold[i].substring(2, bold[i].length - 2) + '</b>');
	            }
	        }

	        var italic = str.match(/[^\\]\*[^*].*?\*/g);  
	        if (italic) {
	            for (i = 0, len = italic.length; i < len; i++) {
	                str = str.replace(italic[i], '<i>' +italic[i].substring(0,1)+ italic[i].substring(2, italic[i].length - 1) + '</i>');
	            }
	        }
	        str = str.replace(/\\\*/g,'*');//去除为*号转义的斜杠
	        var code = str.match(/[^\\]`.+?`/g);
	        if (code) {
	            for (i = 0, len = code.length; i < len; i++) {
	                str = str.replace(code[i], '<code>' +code[i].substring(0,1) + code[i].substring(2, code[i].length - 1) + '</code>');
	            }
	        }
	        str = str.replace(/\\\`/g,'`');//去除为`号转义的斜杠
	        var mark = str.match(/==.+?==/g);
	        if (mark) {
	            for (i = 0, len = mark.length; i < len; i++) {
	                str = str.replace(mark[i], '<mark>' + mark[i].substring(2, mark[i].length - 2) + '</mark>');
	            }
	        }
	        //str = str.replace(/\\\=/g,'=');//去除为=号转义的斜杠
	        var img = str.match(/!\[.*\]\(.*\)/g);
	        var re1 = /\(.*\)/;
	        var re2 = /\[.*\]/;
	        if (img) {
	            for (i = 0, len = img.length; i < len; i++) {
	                var url = img[i].match(re1)[0];
	                var title = img[i].match(re2)[0];
	                str = str.replace(img[i], '<img src=' + url.substring(1, url.length - 1) + ' alt=' + title.substring(1, title.length -1) + '>');
	            }
	        }

	        var a = str.match(/\[.*\]\(.*\)/g);
	        if (a) {
	            for (i = 0, len = a.length; i < len; i++) {
	                var url = a[i].match(re1)[0];
	                var title = a[i].match(re2)[0];
	                str = str.replace(a[i], '<a href=' + url.substring(1, url.length - 1)  + '>' + title.substring(1, title.length -1) + '</a>');
	            }
	        }

	        return str;
	    }
	}
	/************************************************
	 *加载文件的时候在编辑区显示内容，默认渲染编辑区的文本，
	 *如果点击了‘全页面预览按钮’进入全页面预览，且编辑区没
	 *有更新，则渲染内容以文件为准，这时候可以用其他编辑器
	 *编辑md文件，保存文件后预览区会更新显示。当返回编辑区
	 *后，编辑区会重新加载文件内容，此时预览区以编辑区内容
	 *为准。
	 * 总的原则是渲染区的内容总是以最新的内容为准，同时编辑
	 * 的时候(编辑区修改和修改文件)，以网页的编辑区为准。
	************************************************/
	var file_handle;
	var last_modified_time;
	var view_source = 0; //初始状态
	function time_interrupt_cbk(){
		//view_source为2，且文件时间比当前记录的时间晚，则从文件读取内容进行渲染
		if((view_source == 2) && (last_modified_time < file_handle.files[0].lastModified)){
			last_modified_time = file_handle.files[0].lastModified;
			var reader = new FileReader(); //实例化。
			reader.onloadend = function(event) { // 	onloadend事件为：无论文件是否读取成功，读取完后触发。
	        	        if (event.target.readyState == FileReader.DONE) {
	        	        	var div_area = document.getElementById('right_div');
	        	        	div_area.innerHTML = new Markdown(" "," ").compile(event.target.result);
	        	        }
	        	    };
	        reader.readAsText(file_handle.files[0], 'UTF-8');
		}
	}
	function local_preview(){
		var txt_area = document.getElementById('content');
		var div_area = document.getElementById('right_div');
		if(txt_area.style.display == 'none'){ //并排显示
			txt_area.style.display = 'inline-block';
			div_area.style.width = '50%';
			var reader = new FileReader(); //实例化。
			reader.onloadend = function(event) { // 	onloadend事件为：无论文件是否读取成功，读取完后触发。
	        	        if (event.target.readyState == FileReader.DONE) {
	        	        	txt_area.value = event.target.result;
	        	        }
	        	    };
	        reader.readAsText(file, 'UTF-8');
		} else{ //只显示渲染区
			txt_area.style.display = 'none';
			div_area.style.width = '100%';
			if(view_source != 3){//编辑区没有被更改过
				if(file_handle){//file_handle为空则不开启定时器
					var intrrupt = setInterval('time_interrupt_cbk()',1000);
				}
				view_source = 2;//2代表渲染内容从文件获取
			}
		}
	}
	new Markdown($('textarea'), $('#right_div')).init();//一直监听文本输入并转换
	var upload = document.getElementById('upload');	
	upload.onchange = function() {//选择文件时读取文件内容到编辑框中
           var content = document.getElementById('content');
           file_handle = this;
           last_modified_time = file_handle.files[0].lastModified - 1;
           getFileContent(this, function(str) {
               content.value = str;
           });
    }; 
    function converst_to_html() {
	    window.alert('即将转化为html，所需时间和文本大小有关，请稍等一会'); 
	    var html='<html class="gen_html">\n\
	    			<head>\n\
    				<meta charset="utf-8">\n\
    				<title>转换结果</title>\n\
    				<link rel="stylesheet" href="index.css">\n\
    				</head>\n\
    				<body class="gen_body">\n';
		var text = document.getElementById('content').value;
		html += new Markdown(" "," ").compile(text);
		html += '</body>\n</html>\n'
		exportRaw("result.html",html);
	    //markdown渲染
	    new Markdown($('textarea'), $('#right_div')).init(); 
  	}
  	function save_to_txt(){
  		var text = document.getElementById('content').value;
  		exportRaw("*.md",text);
  	}
  	/*===================== 全页面预览快捷键处理 =============*/
  	function hotkey() {
		var a=window.event.keyCode;
		var html_body='';
		var text = '';
		if((a==77)&&(event.ctrlKey)&&(event.altKey)){
			//alert("你按了ctrl+m键吧");
			if(window.localStorage) {                 
             	text = document.getElementById('content').value;
				html_body = new Markdown(" "," ").compile(text);
				localStorage.setItem('preview_body_data',html_body);
				window.location.href="./full_preview.html";
            } else { 
                alert("浏览器不支持 localStorage，无法进行全页面预览。"); 
            } 	
		}
		//else{console.log(e);}
	}// end hotkey
  	document.onkeydown = hotkey; //当onkeydown 事件发生时调用hotkey函数
	</script>
</body>
</html>